<?php
namespace App\Services;

use App\Lib\BCMathLib;
use App\Models\DictInterface;
use App\Models\DictPayment;
use App\Models\RechargeIf;
use App\Models\RechargeOrder;
use App\Repositories\Criteria\DateRangeFilterCriteria;
use App\Repositories\Criteria\MchInfoFilterCriteria;
use App\Repositories\Criteria\OrderStatusCriteria;
use App\Repositories\Criteria\TimeFilterCriteria;
use App\Repositories\Eloquent\RechargeOrderRepositoryEloquent;
use Carbon\Carbon;
use Encore\Admin\Widgets\Table;
use Illuminate\Support\Collection;

class OrderAnalysisService
{
    protected $bcLib;
    protected $orderRepo;

    public function __construct(RechargeOrderRepositoryEloquent $orderRepositoryEloquent, BCMathLib $BCMathLib)
    {
        $this->orderRepo = $orderRepositoryEloquent;
        $this->bcLib = $BCMathLib;
    }

    public function todayDataInAdmin()
    {
        $orders = RechargeOrder::select('uid', 'order_amt', 'order_settle', 'order_rate', 'proxy_settle',
            'is_settle', 'business_settle', 'order_status', 'upper_rate', 'upper_settle')
            ->today()
            ->get();
        $order_num = $orders->count();
        $order_amt = $orders->sum('order_amt');
        $mch_amt = $orders->sum('order_settle');
        $plat_all_settle = $orders->sum('upper_settle'); //平台总所得
        $proxy = clone $orders;
        $proxy = $proxy->where('proxy', '<>', 0);
        $mch_settle = $proxy->sum('order_settle');
        $proxy_settle = $proxy->sum('proxy_settle');
        $proxy_amt = bcsub($proxy_settle, $mch_settle, 6);
        $plat_amt = bcsub($plat_all_settle, $mch_amt, 6);
        $plat_all_profit = bcsub($plat_amt, $proxy_amt, 6); //平台总利润

        $orders = $orders->where('order_status', 1);
        $done_num = $orders->count();
        $done_amt = $orders->sum('order_amt');
        $mch_done_amt = $orders->sum('order_settle');
        $plat_done_settle = $orders->sum('upper_settle');
        $proxy = $proxy->where('order_status', 1);
        $mch_settle = $proxy->sum('order_settle');
        $proxy_settle = $proxy->sum('proxy_settle');
        $proxy_done_amt = bcsub($proxy_settle, $mch_settle, 6);
        $plat_done_amt = bcsub($plat_done_settle, $mch_done_amt, 6);
        $plat_done_profit = bcsub($plat_done_amt, $proxy_done_amt, 6);

        $orders = $orders->where('is_settle', 1);
        $mch_done_settle = $orders->where('is_settle', 1)->sum('order_settle');
        $proxy = $proxy->where('is_settle', 1);
        $mch_settle = $proxy->sum('order_settle');
        $proxy_settle = $proxy->sum('proxy_settle');
        $proxy_done_settle = bcsub($proxy_settle, $mch_settle, 6);



        $data = compact('order_num', 'done_num', 'order_amt',
            'done_amt','mch_amt', 'mch_done_amt', 'mch_done_settle',
            'proxy_amt', 'proxy_done_amt', 'proxy_done_settle',
            'plat_all_settle', 'plat_all_profit', 'plat_done_settle', 'plat_done_profit');
        return $data;
    }

    public function getLineProperties($type = 'finance')
    {
        if ($type == 'finance') {
            return ['总成功金额','平台结算金额','商户收入','代理收入','平台收入'];
        } elseif ($type == 'order') {
            return ['总提交订单', '总成功订单', '待结算订单', '失败订单'];
        }
    }

    //财务线性图标
    public function getFinanceLineData($postData)
    {

        $this->orderRepo->pushCriteria(new DateRangeFilterCriteria($postData['start_at'], $postData['end_at']));
        $this->orderRepo->pushCriteria(new MchInfoFilterCriteria($postData['mchInfo'], ['code', 'username']));
        $this->orderRepo->pushCriteria(new OrderStatusCriteria(1, 'order_status'));
        $orders = $this->orderRepo
            ->orderBy('created_at', 'asc')
            ->get([
                'order_amt', 'created_at', 'order_settle', 'proxy',
                'proxy_settle', 'business', 'business_settle', 'upper_settle'
            ]);
        $accuracy = $postData['accuracy'];
        $start = Carbon::parse($postData['start_at']);
        $getItem = [];
        $total_amt =[];
        $plat_settle =[];
        $mch_settle =[];
        $proxy_profit =[];
        $plat_profit =[];
        $initXaxisPropFunc = function ($key) use (
            &$total_amt, &$plat_settle, &$mch_settle,
            &$proxy_profit, &$plat_profit
        ){
            $total_amt[$key] = 0;
            $plat_settle[$key] = 0;
            $mch_settle[$key] =0;
            $proxy_profit[$key] =0;
            $plat_profit[$key] =0;
        };
        $rangeArr = function ($diff) {
            return range(0, $diff);
        };
        if ($accuracy == 1) {
            $diff = Carbon::parse($postData['end_at'])->diffInHours($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m/d H时');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subHour($arr)->format('Y/m/d H时');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        } elseif ($accuracy == 2) {
            $diff = Carbon::parse($postData['end_at'])->diffInDays($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m/d');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subDay($arr)->format('Y/m/d');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        } elseif ($accuracy == 3) {
            $diff = Carbon::parse($postData['end_at'])->diffInMonths($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subMonth($arr)->format('Y/m');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        }
        $xAxis = array_keys($getItem);
        foreach ($groupOrder as $key => $orders)
        {
            $hourOrder = clone $orders;
//            $hourOrder = $hourOrder->where('created_at', '>=', date(sprintf($formats, $start)))
//                ->where('created_at', '<', date(sprintf($formats, $end)));
            $total_amt[$key] = bcdiv($hourOrder->sum('order_amt'), 1, 2);
            $upperSettle = bcdiv($hourOrder->sum('upper_settle'), 1, 6);
            $plat_settle[$key] = $upperSettle;
            $mchSettle = bcdiv($hourOrder->sum('order_settle'), 1, 6);
            $mch_settle[$key] = $mchSettle;
            //proxy
            $proxyOrder = clone $hourOrder;
            $proxy_settle = $proxy_mch_settle = 0;
            $proxyOrder = $proxyOrder->where('proxy', '<>', 0)->each(function ($item, $key) use ( & $proxy_settle, & $proxy_mch_settle) {
                if (bccomp($item->proxy_settle, $item->order_settle, 6) == 1) {
                    $proxy_settle = bcadd($proxy_settle, $item->proxy_settle, 6);
                    $proxy_mch_settle = bcadd($proxy_mch_settle, $item->order_settle, 6);
                }
            });
            $proxyProfit = bcsub($proxy_settle, $proxy_mch_settle, 6);
            $proxy_profit[$key] = $proxyProfit;

            $plat_profit[$key] = bcsub($upperSettle, bcadd($proxyProfit, $mchSettle, 6), 6);
        }
        $lineProperty = $this->getLineProperties();

        $totalAmt = $this->formatSeriesData($lineProperty[0], array_values($total_amt));
        $platSettle = $this->formatSeriesData($lineProperty[1], array_values($plat_settle));
        $mchSettle = $this->formatSeriesData($lineProperty[2], array_values($mch_settle));
        $proxyProfit = $this->formatSeriesData($lineProperty[3], array_values($proxy_profit));
        $platProfit = $this->formatSeriesData($lineProperty[4], array_values($plat_profit));
        $series = $totalAmt + $platSettle + $mchSettle + $proxyProfit + $platProfit;

        $data = compact('xAxis', 'series', 'lineProperty');
        return $data;
    }

    protected function formatSeriesData($lineName, $singleSeries)
    {
        return [
            $lineName => $singleSeries
        ];
    }


    public function getOrderLineData($postData)
    {
        $this->orderRepo->pushCriteria(new DateRangeFilterCriteria($postData['start_at'], $postData['end_at']));
        $this->orderRepo->pushCriteria(new MchInfoFilterCriteria($postData['mchInfo'], ['code', 'username']));
//        $this->orderRepo->pushCriteria(new OrderStatusCriteria(1, 'order_status'));
        $orders = $this->orderRepo
            ->orderBy('created_at', 'asc')
            ->get(['is_settle', 'created_at', 'order_status']);
        $accuracy = $postData['accuracy'];
        $start = Carbon::parse($postData['start_at']);
        $getItem = [];
        $total_order =[];
        $done_order =[];
        $failed_order =[];
        $settling_order =[];
        $initXaxisPropFunc = function ($key) use (
            &$total_order, &$done_order, &$failed_order,
            &$settling_order
        ){
            $total_order[$key] = 0;
            $done_order[$key] = 0;
            $failed_order[$key] =0;
            $settling_order[$key] =0;
        };
        $rangeArr = function ($diff) {
            return range(0, $diff);
        };
        if ($accuracy == 1) {
            $diff = Carbon::parse($postData['end_at'])->diffInHours($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m/d H时');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subHour($arr)->format('Y/m/d H时');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        } elseif ($accuracy == 2) {
            $diff = Carbon::parse($postData['end_at'])->diffInDays($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m/d');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subDay($arr)->format('Y/m/d');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        } elseif ($accuracy == 3) {
            $diff = Carbon::parse($postData['end_at'])->diffInMonths($start);
            $rangeArr = $rangeArr($diff);
            krsort($rangeArr);
            $xAxisArr = new Collection($rangeArr);
            $groupOrder = $orders->groupBy(function ($item, $key) {
                return Carbon::parse($item['created_at'])->format('Y/m');
            });
            foreach ($xAxisArr->chunk(200) as $chunk) {
                foreach ($chunk as $arr) {
                    $key = Carbon::parse($postData['end_at'])->subMonth($arr)->format('Y/m');
                    $getItem[$key] = [];
                    $initXaxisPropFunc($key);
                }
            }
        }
        $xAxis = array_keys($getItem);

        // $total_order =[];
//        $done_order =[];
//        $failed_order =[];
//        $settling_order =[];
        foreach ($groupOrder as $key => $orders)
        {
            $total_order[$key] = $orders->count();
            $done_order[$key] = $orders->where('order_status', 1)->count();
            $failed_order[$key] = $orders->where('order_status', '<>', 1)->count();
            $settling_order[$key] = $orders->where('order_status', 1)->where('is_settle', 0)->count();
        }
        $lineProperty = $this->getLineProperties('order');

        $totalOrder = $this->formatSeriesData($lineProperty[0], array_values($total_order));
        $doneOrder = $this->formatSeriesData($lineProperty[1], array_values($done_order));
        $settlingOrder = $this->formatSeriesData($lineProperty[2], array_values($settling_order));
        $failedOrder = $this->formatSeriesData($lineProperty[3], array_values($failed_order));
        $series = $totalOrder + $doneOrder + $failedOrder + $settlingOrder;

        $data = compact('xAxis', 'series', 'lineProperty');
        return $data;

    }

    public function getPaymentsTableData($postData)
    {
        $this->orderRepo->pushCriteria(new DateRangeFilterCriteria($postData['start_at'], $postData['end_at']));
        $this->orderRepo->pushCriteria(new MchInfoFilterCriteria($postData['mchInfo'], ['code', 'username']));
        $heading = ['通道名称', '总提交笔数', '总提交金额', ' 总成功笔数', '总成功金额', '总平台结算金额', '平台通道利润', '成功率'];
        $payments = DictPayment::recharge()->orderBy('order', 'desc')->get(['name', 'id', 'identify'])->pluck('name', 'id');
        $orders = $this->orderRepo
            ->all(['pm_id', 'order_amt', 'order_settle', 'proxy', 'proxy_settle', 'upper', 'upper_settle', 'order_status']);
        $interfacesTable = $this->getInterfacesTableData($orders);
        $rows = [];
        foreach ($payments as $key => $pa) {
            $rows[$key] = [
                'name' => $pa,
                'total_num' =>0,
                'total_amt' =>0,
                'done_num' =>0,
                'done_amt' =>0,
                'plat_settle' =>0,
                'plat_profit' =>0,
                'rate' => '0%'
            ];
        }

        //各通道总量
        $all = [
            'name' => '总计',
            'total_num' =>0,
            'total_amt' =>0,
            'done_num' =>0,
            'done_amt' =>0,
            'plat_settle' =>0,
            'plat_profit' =>0,
            'rate' => '-'
        ];

        foreach ($orders->chunk(200) as $order) {
            $paymentOrder = $order->groupBy('pm_id');
            foreach ($paymentOrder as $key => $py) {
                $total_num = $py->count();
                $total_amt =  bcdiv($py->sum('order_amt'), 1, 2);
                $doneOrder = clone $py;
                $doneOrder = $doneOrder->where('order_status', 1);
                $done_num = $doneOrder->count();
                $done_amt = bcdiv($doneOrder->sum('order_amt'), 1, 2);
                $upper_settle = bcdiv($doneOrder->sum('upper_settle'), 1, 6);
                $plat_profit = 0;
                $doneOrder->each(function ($item, $key) use ( & $plat_profit) {
                    if ($item->proxy) {
                        $plat_profit = bcadd($plat_profit, bcsub($item->upper_settle, $item->proxy_settle, 6), 6);
                    } else {
                        $plat_profit = bcadd($plat_profit, bcsub($item->upper_settle, $item->order_settle, 6), 6);
                    }
                });
//                $rate = @bcdiv($done_num, $total_num, 4) * 100;
                $all['total_num'] += $total_num;
                $all['done_num'] += $done_num;
                $all['total_amt'] = bcadd($all['total_amt'], $total_amt, 2);
                $all['done_amt'] = bcadd($all['done_amt'], $done_amt, 2);
                $all['plat_settle'] = bcadd($all['plat_settle'], $upper_settle, 6);
                $all['plat_profit'] = bcadd($all['plat_profit'], $plat_profit, 6);
//                $all['rate'] = bcadd($all['rate'], $rate, 4);
                $rows[$key]['total_num'] += $total_num;
                $rows[$key]['total_amt'] = bcadd($rows[$key]['total_amt'], $total_amt, 2);
                $rows[$key]['done_num'] += $done_num;
                $rows[$key]['done_amt'] = bcadd($rows[$key]['done_amt'], $done_amt, 2);
                $rows[$key]['plat_settle'] =bcadd($rows[$key]['plat_settle'], $upper_settle, 6);
                $rows[$key]['plat_profit'] = bcadd($rows[$key]['plat_profit'], $plat_profit, 6);
//                $rows[$key]['rate'] = bcadd($rows[$key]['rate'], $rate, 4);
            }
        }
        foreach ($rows as $k =>  $row) {
            $row['rate'] = @bcdiv($row['done_num'], $row['total_num'], 4) * 100 . '%';
            $rows[$k] = array_values($row);
        }
        array_push($rows, array_values($all));
        $table = new Table($heading, array_values($rows));
        return ['payment' => $table->render(), 'interface' => $interfacesTable];
    }


    public function getInterfacesTableData($orders)
    {
//        $this->orderRepo->pushCriteria(new DateRangeFilterCriteria($postData['start_at'], $postData['end_at']));
//        $this->orderRepo->pushCriteria(new MchInfoFilterCriteria($postData['mchInfo'], ['code', 'username']));
//        $orders = $this->orderRepo
//            ->all(['pm_id', 'order_amt', 'order_settle', 'proxy', 'proxy_settle', 'upper', 'upper_settle', 'order_status']);
        $interfaces = RechargeIf::get(['id', 'name', 'identify'])->pluck('name', 'id');
        $payments = DictPayment::recharge()->get(['id', 'name', 'identify'])->pluck('name', 'id');
        $heading[] = '接口商名称';
        $heading = array_merge($heading, array_values($payments->toArray()));
        array_push($heading, '总成功订单/总订单', '总成功金额/总金额', '平台总结算/平台利润');
        $rows = [];
        $parseRowStr = function ($done_num, $total_num, $done_amt, $total_amt){
            $rate = @bcdiv($done_num, $total_num, 4) * 100 .'%';
            $str = "%d/%d  <br/>$ %s/%s  <br/>R:%s";
            return sprintf($str, $done_num, $total_num, $done_amt, $total_amt, $rate);
        };
        $lastAnalysis = [];
        $singlePaymentMap = [];
        foreach ($interfaces as $key => $interface) {
            $lastAnalysis[$key] = [
                'total_num' => 0,
                'done_num' => 0,
                'done_amt' => 0,
                'total_amt' => 0,
                'plat_settle' => 0,
                'plat_profit' => 0,
            ];
            $row['name'] = $interface;
            foreach ($payments as $k => $payment) {
                $singlePaymentMap[$key][$k] = [
                    'total_num' => 0,
                    'done_num' => 0,
                    'done_amt' => 0,
                    'total_amt' => 0,
                ];
                $row[$k] = $parseRowStr(0, 0, 0, 0);
            }
            $row += ['order' => '0/0', 'amt' => '0/0', 'settle' => '0/0'];
            $rows[$key] = $row;
        }

        foreach ($orders->chunk(200) as $order) {
            $upperOrder = $order->groupBy('upper');
            foreach ($upperOrder as $key => $upOrder) {
                $ptOrder = $upOrder->groupBy('pm_id');
//                $row = $rows[$key];
                foreach ($ptOrder as $p_key => $pOrder) {
                    $singlePaymentMap[$key][$p_key]['total_num'] += $pOrder->count();
                    $order_amt = bcdiv($pOrder->sum('order_amt'), 1, 2);
                    $singlePaymentMap[$key][$p_key]['total_amt'] = bcadd($order_amt, $singlePaymentMap[$key][$p_key]['total_amt'], 2);
                    $singlePaymentMap[$key][$p_key]['done_num'] += $pOrder->where('order_status', 1)->count();
                    $done_amt = bcdiv($pOrder->where('order_status', 1)->sum('order_amt'), 1, 2);
                    $singlePaymentMap[$key][$p_key]['done_amt'] = bcadd($singlePaymentMap[$key][$p_key]['done_amt'], $done_amt, 2);
//                    $row[$p_key] = $parseRowStr($done_num, $total_num, $done_amt, $total_amt);
                }

                $total_num = $upOrder->count();
                $total_amt = bcdiv($upOrder->sum('order_amt'), 1, 2);
                $done_num = $upOrder->where('order_status', 1)->count();
                $done_amt = bcdiv($upOrder->where('order_status', 1)->sum('order_amt'), 1, 2);
                $settle_amt = bcdiv($upOrder->where('order_status', 1)->sum('upper_settle'), 1, 6);
                $lastAnalysis[$key]['total_num'] += $total_num;
                $lastAnalysis[$key]['done_num'] += $done_num;
                $lastAnalysis[$key]['total_amt'] = bcadd($lastAnalysis[$key]['total_amt'], $total_amt, 2);
                $lastAnalysis[$key]['done_amt'] = bcadd($lastAnalysis[$key]['done_amt'] , $done_amt, 2);
                $lastAnalysis[$key]['plat_settle'] = bcadd($settle_amt, $lastAnalysis[$key]['plat_settle'], 6);
                $platProfit = 0;
                $upOrder->where('order_status', 1)->each(function ($item, $key) use (& $platProfit) {
                    if ($item->proxy) {
                        $platProfit = bcadd($platProfit, bcsub($item->upper_settle, $item->proxy_settle, 6), 6);
                    } else {
                        $platProfit = bcadd($platProfit, bcsub($item->upper_settle, $item->order_settle, 6), 6);
                    }
                });
                $lastAnalysis[$key]['plat_profit'] = bcadd($platProfit, $lastAnalysis[$key]['plat_profit'], 6);
            }
        }
        foreach ($lastAnalysis as $key => $analysis) {
            $rows[$key]['order'] = $analysis['done_num'] . '/' . $analysis['total_num'];
            $rows[$key]['amt'] = $analysis['done_amt'] . '/' . $analysis['total_amt'];
            $rows[$key]['settle'] = $analysis['plat_settle'] . '/' . $analysis['plat_profit'];
            $paymentMap = $singlePaymentMap[$key];
            foreach ($paymentMap as $k => $map) {
                $rows[$key][$k] = $parseRowStr($map['done_num'], $map['total_num'], $map['done_amt'], $map['total_amt']);
            }
        }
        foreach ($rows as $k =>  $row) {
            $rows[$k] = array_values($row);
        }
        $table = new Table($heading, array_values($rows));
        return $table->render();
    }

}
